package com.upcode.weighing.redisson.config;


import com.upcode.weighing.redisson.RedisKeyExpirationListener;
import com.upcode.weighing.redisson.properties.RedisProperties;
import com.upcode.weighing.redisson.service.DistributedLocker;
import com.upcode.weighing.redisson.service.impl.DistributedLockerImpl;
import com.upcode.weighing.redisson.util.RedisLockUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 自动装配类
 */
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@Slf4j
public class RedissonAutoConfiguration {

    @Autowired
    private RedisProperties redisProperties;

    @Configuration
    @ConditionalOnClass({Redisson.class})
    @ConditionalOnExpression("'${spring.redisson.mode}'=='single' or '${spring.redisson.mode}'=='cluster' or '${spring.redisson.mode}'=='sentinel'")
    protected class RedissonSingleClientConfiguration {
        /**
         * 单机模式 redisson 客户端
         */
        @Bean
        @ConditionalOnProperty(name = "spring.redisson.mode", havingValue = "single")
        RedissonClient redissonSingle() {
            Config config = new Config();
            String node = redisProperties.getSingle().getAddress();
            node = node.startsWith("redis://") ? node : "redis://" + node;
            SingleServerConfig serverConfig = config.useSingleServer()
                    .setAddress(node)
                    .setTimeout(redisProperties.getPool().getConnTimeout())
                    .setConnectionPoolSize(redisProperties.getPool().getSize())
                    .setDatabase(redisProperties.getDatabase())
                    .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
                serverConfig.setPassword(redisProperties.getPassword());
            }
            return Redisson.create(config);
        }

        /**
         * 集群模式的 redisson 客户端
         *
         * @return
         */
        @Bean
        @ConditionalOnProperty(name = "spring.redisson.mode", havingValue = "cluster")
        RedissonClient redissonCluster() {
            log.info("cluster redisProperties:" + redisProperties.getCluster());
            Config config = new Config();
            String[] nodes = redisProperties.getCluster().getNodes().split(",");
            List<String> newNodes = new ArrayList(nodes.length);
            Arrays.stream(nodes).forEach((index) -> newNodes.add(
                    index.startsWith("redis://") ? index : "redis://" + index));

            ClusterServersConfig serverConfig = config.useClusterServers()
                    .addNodeAddress(newNodes.toArray(new String[0]))
                    .setScanInterval(
                            redisProperties.getCluster().getScanInterval())
                    .setIdleConnectionTimeout(
                            redisProperties.getPool().getSoTimeout())
                    .setConnectTimeout(
                            redisProperties.getPool().getConnTimeout())
                    .setFailedSlaveCheckInterval(
                            redisProperties.getCluster().getFailedAttempts())
                    .setRetryAttempts(
                            redisProperties.getCluster().getRetryAttempts())
                    .setRetryInterval(
                            redisProperties.getCluster().getRetryInterval())
                    .setMasterConnectionPoolSize(redisProperties.getCluster()
                            .getMasterConnectionPoolSize())
                    .setSlaveConnectionPoolSize(redisProperties.getCluster()
                            .getSlaveConnectionPoolSize())
                    .setMasterConnectionMinimumIdleSize(5)
                    .setSlaveConnectionMinimumIdleSize(1)
                    .setTimeout(redisProperties.getTimeout());
            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
                serverConfig.setPassword(redisProperties.getPassword());
            }
            return Redisson.create(config);
        }


        /**
         * 装配locker类，并将实例注入到RedissLockUtil中
         *
         * @return
         */
        @Bean
        DistributedLocker distributedLocker(RedissonClient redissonClient) {
            DistributedLocker locker = new DistributedLockerImpl();
            locker.setRedissonClient(redissonClient);
            RedisLockUtil.setLocker(locker);
            return locker;
        }


    }

    @Configuration
    public class RedisListenerConfig {
        @Bean
        RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {

            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            // 事件以__keyevent@<db>__为前缀进行发布
            container.addMessageListener(new RedisKeyExpirationListener(container), new PatternTopic("__keyevent@0__" +
                    ":expired"));
            return container;
        }
    }
}
